package com.hx.hash;

import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @author MinghuaChen
 * @date 2022-03-06 21:59
 * @description 一致性hash不带虚拟节点
 */
public class ConsistentHashingWithoutVirtualNode {


    private static SortedMap<Integer, String> sortedMap = new TreeMap<>();

    private static String[] servers = {
            "192.168.26.81",
            "192.168.26.82",
            "192.168.26.83",
            "192.168.26.84",
            "192.168.26.85",
    };


    static {
        for (String server : servers) {
            int hash = getHash(server);
            System.out.println("[" + server + "]加入集合,其hash值为" + hash);
            sortedMap.put(hash, server);
        }
        System.out.println();
    }

    public static void main(String[] args) {

        String[] keys = {"太阳", "月亮", "星星"};
        for (int i = 0; i < keys.length; i++)
            System.out.println("[" + keys[i] + "]的hash值为" + getHash(keys[i])
                    + ", 被路由到结点[" + getServerAddress(keys[i]) + "]");
    }


    private static String getServerAddress(String key) {
        // 获取对应的hash值
        int hash = getHash(key);
        // 获取大于该hash值所有的map子集合
        SortedMap<Integer, String> subMap = sortedMap.tailMap(hash);
        // subMap为空  返回所有map的第一个
        if (subMap.isEmpty()) {
            Integer integer = sortedMap.firstKey();
            return sortedMap.get(integer);
        } else {
            // 返回顺时针节点最近的那个node
            Integer integer = subMap.firstKey();
            return subMap.get(integer);
        }
    }


    /**
     * 获取hash值
     *
     * @param server 服务器地址
     * @return
     */
    private static int getHash(String server) {
        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < server.length(); i++)
            hash = (hash ^ server.charAt(i)) * p;
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;

        // 如果算出来的值为负数则取其绝对值
        if (hash < 0)
            hash = Math.abs(hash);
        return hash;
    }


}
